/*
Copyright 2022 The Karmada Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
	"bytes"
	"context"
	"encoding/pem"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/stretchr/testify/assert"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/client-go/rest"
	controllerruntime "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"

	clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
	"github.com/karmada-io/karmada/pkg/util/gclient"
)

// Generated by
//
//	openssl genrsa 2048 > ca.key
//	openssl req -new -x509 -nodes -days 365000 -key ca.key -out ca.crt
var testCA = []byte(`-----BEGIN CERTIFICATE-----
MIIDlTCCAn2gAwIBAgIULtNqpv91jFAqaYIcKC3+2B3PalwwDQYJKoZIhvcNAQEL
BQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJMTI3LjAuMC4xMCAX
DTI1MDEyNzA5NTAzMFoYDzMwMjQwNTMwMDk1MDMwWjBZMQswCQYDVQQGEwJBVTET
MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
dHkgTHRkMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDBMshenKkdcJPzAiIcbSjbtoNppdMvOQoEY+EAwz2xZf1cYuJN
86roVXb+YI7NEN1IZcP2T1BJR4/l1QS9AC2bhssss6ANEsQRKFbL+F71GWvrD8SU
nqsEgtceZkMWahFALgFZ5msBdHlibtmiqZEt7vXiHGK5hVVpdWLHBR6vYcxpzH0g
wSnGTz0Y1vkN/vZ/sS/NPQnfrDMwmF7M5z/Y5HtWQYeKklYnjUS5bcmXWU1Cal2Q
DSZwhgdr/BfmtsBG8PXu/0T2DchQi6UwO6S2yLeVN6QOdgC6GRO80L4M4t7h3BoB
WI5BrUIHiqnIPkKCk46WgdjXGhi0r5a1MqE7AgMBAAGjUzBRMB0GA1UdDgQWBBQa
I7a0H4qe70U2kEW/XpiqXG+diDAfBgNVHSMEGDAWgBQaI7a0H4qe70U2kEW/Xpiq
XG+diDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAlGvFxY8zI
yT+YUHQ0Kbg6EKeXf/t36dMKA+cmWSkBK26q4rBu1M8SoICbLpkjtvDsxGgylOll
HfCM08wFDELnJdAIKKcqXqP9tMG9RoUBxKIh1dmTHBAOpAN+RXCYHwiFNLTFLbQI
GsZ3Cl3vF5RBzkFHGE/fV1m6y1+DUOGAn2meQVZR5epW5aU+fd7eodhF9ck9hfHW
qpUIgHpXjWb1Lb6w2Y19eK/4kkMTlcj/XWmQfgXFgVUANUsCDIu/ftoYY9eO6ymu
qS+QTm6FNs0/71qf9muggh4jLnCjlXd9/aCQNPZ7p/m3Te9tpVQaEkTtu8Qe4g1j
nQi8/QKAuCtv
-----END CERTIFICATE-----`)

func TestNewClusterScaleClientSet(t *testing.T) {
	type args struct {
		clusterName string
		client      client.Client
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
		errMsg  string
	}{
		{
			name: "cluster not found",
			args: args{
				clusterName: "test",
				client:      fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).Build(),
			},
			wantErr: true,
			errMsg:  "clusters.cluster.karmada.io \"test\" not found",
		},
		{
			name: "APIEndpoint is empty",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).
					WithObjects(newCluster("test")).Build(),
			},
			wantErr: true,
			errMsg:  "the api endpoint of cluster test is empty",
		},
		{
			name: "SecretRef is empty",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).
					WithObjects(withAPIEndPoint(newCluster("test"), "https://127.0.0.1")).Build(),
			},
			wantErr: true,
			errMsg:  "cluster test does not have a secret",
		},
		{
			name: "Secret not found",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "default", Name: "secret1"},
						},
					}).Build(),
			},
			wantErr: true,
			errMsg:  "secrets \"secret1\" not found",
		},
		{
			name: "token not found",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
						},
					},
					&corev1.Secret{
						ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
					}).Build(),
			},
			wantErr: true,
			errMsg:  "the secret for cluster test is missing a non-empty value for \"token\"",
		},
		{
			name: "valid configuration",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
						},
					},
					&corev1.Secret{
						ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
						Data:       map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA},
					}).Build(),
			},
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := NewClusterScaleClientSet(tt.args.clusterName, tt.args.client)
			if tt.wantErr {
				assert.Error(t, err)
				assert.Nil(t, got)
				assert.Contains(t, err.Error(), tt.errMsg)
			} else {
				assert.NoError(t, err)
				assert.NotNil(t, got)
				assert.Equal(t, tt.args.clusterName, got.ClusterName)
				assert.NotNil(t, got.KubeClient)
				assert.NotNil(t, got.ScaleClient)
			}
		})
	}
}

func TestNewClusterClientSetForAgent(t *testing.T) {
	type args struct {
		clusterName  string
		client       client.Client
		clientOption *ClientOption
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "valid configuration",
			args: args{
				clusterName:  "test-agent",
				client:       fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).Build(),
				clientOption: &ClientOption{},
			},
			wantErr: false,
		},
	}

	// Store the original GetConfig function
	originalGetConfig := controllerruntime.GetConfig
	// Defer its restoration
	defer func() { controllerruntime.GetConfig = originalGetConfig }()

	// Mock the GetConfig function
	controllerruntime.GetConfig = func() (*rest.Config, error) {
		return &rest.Config{
			Host: "https://fake.example.com",
		}, nil
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := NewClusterClientSetForAgent(tt.args.clusterName, tt.args.client, tt.args.clientOption)
			if tt.wantErr {
				assert.Error(t, err)
				assert.Nil(t, got)
			} else {
				assert.NoError(t, err)
				assert.NotNil(t, got)
				assert.Equal(t, tt.args.clusterName, got.ClusterName)
				assert.NotNil(t, got.KubeClient)
			}
		})
	}
}

func TestNewClusterDynamicClientSetForAgent(t *testing.T) {
	type args struct {
		clusterName         string
		client              client.Client
		clusterClientOption *ClientOption
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "valid configuration",
			args: args{
				clusterName: "test-agent-dynamic",
				client:      fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).Build(),
			},
			wantErr: false,
		},
	}

	// Store the original GetConfig function
	originalGetConfig := controllerruntime.GetConfig
	// Defer its restoration
	defer func() { controllerruntime.GetConfig = originalGetConfig }()

	// Mock the GetConfig function
	controllerruntime.GetConfig = func() (*rest.Config, error) {
		return &rest.Config{
			Host: "https://fake.example.com",
		}, nil
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := NewClusterDynamicClientSetForAgent(tt.args.clusterName, tt.args.client, tt.args.clusterClientOption)
			if tt.wantErr {
				assert.Error(t, err)
				assert.Nil(t, got)
			} else {
				assert.NoError(t, err)
				assert.NotNil(t, got)
				assert.Equal(t, tt.args.clusterName, got.ClusterName)
				assert.NotNil(t, got.DynamicClientSet)
			}
		})
	}
}

func TestNewClusterClientSet(t *testing.T) {
	type args struct {
		clusterName  string
		client       client.Client
		clientOption *ClientOption
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "cluster not found",
			args: args{
				clusterName:  "test",
				client:       fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).Build(),
				clientOption: nil,
			},
			wantErr: true,
		},
		{
			name: "APIEndpoint is empty",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).
					WithObjects(newCluster("test")).Build(),
				clientOption: nil,
			},
			wantErr: true,
		},
		{
			name: "SecretRef is empty",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).
					WithObjects(withAPIEndPoint(newCluster("test"), "https://127.0.0.1")).Build(),
				clientOption: nil,
			},
			wantErr: true,
		},
		{
			name: "Secret not found",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "default", Name: "secret1"},
						},
					}).Build(),
				clientOption: nil,
			},
			wantErr: true,
		},
		{
			name: "token not found",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
						},
					},
					&corev1.Secret{
						ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
					}).Build(),
				clientOption: nil,
			},
			wantErr: true,
		},
		{
			name: "CA data is set",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
						},
					},
					&corev1.Secret{
						ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
						Data:       map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA},
					}).Build(),
				clientOption: &ClientOption{},
			},
			wantErr: false,
		},
		{
			name: "skip TLS verification",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint:                 "https://127.0.0.1",
							SecretRef:                   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
							InsecureSkipTLSVerification: true,
						},
					},
					&corev1.Secret{
						ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
						Data:       map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token")},
					}).Build(),
				clientOption: &ClientOption{},
			},
			wantErr: false,
		},
		{
			name: "ProxyURL is error",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
							ProxyURL:    "://",
						},
					},
					&corev1.Secret{
						ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
						Data:       map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA}}).Build(),
				clientOption: &ClientOption{},
			},
			wantErr: true,
		},
		{
			name: "ProxyURL is set",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
							ProxyURL:    "http://1.1.1.1",
						},
					},
					&corev1.Secret{
						ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
						Data:       map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA},
					}).Build(),
				clientOption: &ClientOption{},
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := NewClusterClientSet(tt.args.clusterName, tt.args.client, tt.args.clientOption)
			if tt.wantErr {
				assert.Error(t, err)
				assert.Nil(t, got)
			} else {
				assert.NoError(t, err)
				assert.NotNil(t, got)
				assert.Equal(t, tt.args.clusterName, got.ClusterName)
				assert.NotNil(t, got.KubeClient)
			}
		})
	}
}

func TestNewClusterClientSet_ClientWorks(t *testing.T) {
	s := httptest.NewTLSServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
		rw.Header().Add("Content-Type", "application/json")
		_, _ = io.WriteString(rw, `
{
    "apiVersion": "v1",
    "kind": "Node",
    "metadata": {
        "name": "foo"
    }
}`)
	}))
	defer s.Close()

	testCA := getCACertFromGTestServer(t, s)

	const clusterName = "test"
	hostClient := fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
		&clusterv1alpha1.Cluster{
			ObjectMeta: metav1.ObjectMeta{Name: clusterName},
			Spec: clusterv1alpha1.ClusterSpec{
				APIEndpoint: s.URL,
				SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
			},
		},
		&corev1.Secret{
			ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
			Data:       map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA},
		}).Build()

	clusterClient, err := NewClusterClientSet(clusterName, hostClient, nil)
	assert.NoError(t, err)
	assert.NotNil(t, clusterClient)

	got, err := clusterClient.KubeClient.CoreV1().Nodes().Get(context.TODO(), "foo", metav1.GetOptions{})
	assert.NoError(t, err)
	assert.NotNil(t, got)

	want := &corev1.Node{
		ObjectMeta: metav1.ObjectMeta{
			Name: "foo",
		},
	}
	assert.Equal(t, want, got)
}

func TestNewClusterDynamicClientSet(t *testing.T) {
	type args struct {
		clusterName         string
		client              client.Client
		clusterClientOption *ClientOption
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "cluster not found",
			args: args{
				clusterName: "test",
				client:      fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).Build(),
			},
			wantErr: true,
		},
		{
			name: "APIEndpoint is empty",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).
					WithObjects(newCluster("test")).Build(),
			},
			wantErr: true,
		},
		{
			name: "SecretRef is empty",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).
					WithObjects(withAPIEndPoint(newCluster("test"), "https://127.0.0.1")).Build(),
			},
			wantErr: true,
		},
		{
			name: "Secret not found",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "default", Name: "secret1"},
						},
					}).Build(),
			},
			wantErr: true,
		},
		{
			name: "token not found",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
						},
					},
					&corev1.Secret{
						ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
					}).Build(),
			},
			wantErr: true,
		},
		{
			name: "CA data is set",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
						},
					},
					&corev1.Secret{
						ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
						Data:       map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA},
					}).Build(),
			},
			wantErr: false,
		},
		{
			name: "skip TLS verification",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
						},
					},
					&corev1.Secret{
						ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
						Data:       map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA},
					}).Build(),
			},
			wantErr: false,
		},
		{
			name: "ProxyURL is error",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
							ProxyURL:    "://",
						},
					},
					&corev1.Secret{
						ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
						Data:       map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA},
					}).Build(),
			},
			wantErr: true,
		},
		{
			name: "ProxyURL is set",
			args: args{
				clusterName: "test",
				client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
					&clusterv1alpha1.Cluster{
						ObjectMeta: metav1.ObjectMeta{Name: "test"},
						Spec: clusterv1alpha1.ClusterSpec{
							APIEndpoint: "https://127.0.0.1",
							SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
							ProxyURL:    "http://1.1.1.1",
						},
					},
					&corev1.Secret{
						ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
						Data:       map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA},
					}).Build(),
			},
			wantErr: false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := NewClusterDynamicClientSet(tt.args.clusterName, tt.args.client, tt.args.clusterClientOption)
			if tt.wantErr {
				assert.Error(t, err)
				assert.Nil(t, got)
			} else {
				assert.NoError(t, err)
				assert.NotNil(t, got)
				assert.Equal(t, tt.args.clusterName, got.ClusterName)
				assert.NotNil(t, got.DynamicClientSet)
			}
		})
	}
}

func TestNewClusterDynamicClientSet_ClientWorks(t *testing.T) {
	s := httptest.NewTLSServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
		rw.Header().Add("Content-Type", "application/json")
		_, _ = io.WriteString(rw, `
{
    "apiVersion": "v1",
    "kind": "Node",
    "metadata": {
        "name": "foo"
    }
}`)
	}))
	defer s.Close()

	testCA := getCACertFromGTestServer(t, s)

	const clusterName = "test"
	hostClient := fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(
		&clusterv1alpha1.Cluster{
			ObjectMeta: metav1.ObjectMeta{Name: clusterName},
			Spec: clusterv1alpha1.ClusterSpec{
				APIEndpoint: s.URL,
				SecretRef:   &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"},
			},
		},
		&corev1.Secret{
			ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"},
			Data:       map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA},
		}).Build()

	clusterClient, err := NewClusterDynamicClientSet(clusterName, hostClient, nil)
	assert.NoError(t, err)
	assert.NotNil(t, clusterClient)

	nodeGVR := corev1.SchemeGroupVersion.WithResource("nodes")
	got, err := clusterClient.DynamicClientSet.Resource(nodeGVR).Get(context.TODO(), "foo", metav1.GetOptions{})
	assert.NoError(t, err)
	assert.NotNil(t, got)

	want := &unstructured.Unstructured{}
	want.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("Node"))
	want.SetName("foo")

	assert.Equal(t, want, got)
}

func getCACertFromGTestServer(t *testing.T, s *httptest.Server) []byte {
	t.Helper()
	testCA := new(bytes.Buffer)
	err := pem.Encode(testCA, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: s.Certificate().Raw,
	})
	assert.NoError(t, err)
	return testCA.Bytes()
}
